package asteroids.entity.equipment;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;

import asteroids.engine.GameState;
import asteroids.engine.Utils;
import asteroids.entity.Ship;
import asteroids.factory.ShapeLib;

/**
 * Shield that is always active, expected to be used as a Special. Completely
 * negates any damage being done, but constantly uses energy when in use. Draws
 * itself whenever active
 * 
 * @author whitecd1
 * 
 */
public class ForceShield extends Equipment
{
    private boolean shielding = false;
    // one energy every 2 frames at 24fps
    private double ENERGY_DRAIN = 24d / Utils.FRAMES_PER_SECOND / 2;

    public ForceShield(Ship ship)
    {
        super(ship);

        drawMe = true; // unlike other equipment, this can be drawn directly

        // create/assign shape,
        Utils.CartesianCoordinate[] shipShape = ship.getCollisionBounds();
        double shipx = ship.getXPosition();
        double shipy = ship.getYPosition();

        objectShape = new Utils.PolarCoordinate[shipShape.length * 2];
        for( int i = 0; i < shipShape.length; i++ )
        {
            // add two points to objectShape, to round hard edges
            objectShape[i * 2] = Utils.polarFromCartesian(shipShape[i].shiftBy(-shipx, -shipy))
                    .scaleBy(1.3).rotateBy(-ship.getRotation() - 3);
            objectShape[i * 2 + 1] = Utils.polarFromCartesian(shipShape[i].shiftBy(-shipx, -shipy))
                    .scaleBy(1.3).rotateBy(-ship.getRotation() + 3);
        }
        objectShape = deriveShape(ship.getObjectShape());

        max_radius = ShapeLib.shipLength;
    }

    public void update(GameState state)
    {
        if( ship.getEnergy() >= ENERGY_DRAIN )
        {
            ship.makeInvincible();
            ship.modifyEnergyBy(ENERGY_DRAIN * -1);
            shielding = true;
        }
    }

    public void respawn()
    {
        // no management necessary
    }

    public void draw(Graphics g)
    {
        if( shielding && ship.isAlive() )
        {
            x_position = ship.getXPosition();
            y_position = ship.getYPosition();
            rotation = ship.getRotation();
            color = Color.WHITE;
            super.draw(g);

            shielding = false;
        }
    }

    private Utils.PolarCoordinate[] deriveShape(Utils.PolarCoordinate[] shipShape)
    {
        ArrayList<Utils.PolarCoordinate> shieldBuilder = new ArrayList<Utils.PolarCoordinate>();

        // address triplicates of points from the ship's objectShape,
        // determining whether the middle point is concave or convex
        // if it is convex, generate two new points in the shield shape, one
        // along each line of incidence, apart from the shape by 2px
        // if it is concave, generate one new point, centered between each line,
        // again 2px from the middle point

        // convert shape to Cartesian- or just take the Cartesian version in
        Utils.CartesianCoordinate[] cShape = Utils.cartesianShapeFromPolarShape(shipShape);

        // grab three points
        for( int c = 0; c < cShape.length; c++ )
        {
            Utils.CartesianCoordinate c1 = cShape[c];
            Utils.CartesianCoordinate c2 = cShape[(c + 1) % cShape.length];
            Utils.CartesianCoordinate c3 = cShape[(c + 2) % cShape.length];

            double dir12 = Utils.pointDirection(c1.getXComponent(), c1.getYComponent(),
                    c2.getXComponent(), c2.getYComponent());
            double dir23 = Utils.pointDirection(c2.getXComponent(), c2.getYComponent(),
                    c3.getXComponent(), c3.getYComponent());

            double turnDistL = (dir23 - dir12 < 0) ? dir23 - dir12 + 360 : dir23 - dir12;
            double turnDistR = (dir12 - dir23 < 0) ? dir12 - dir23 + 360 : dir12 - dir23;

            // get direction
            boolean convex = (turnDistR < turnDistL) ? true : false;

            double shieldDistance = 2;
            // generate 1 or 2 new points at correct locations
            if( convex ) // make two shield points to soften the angle
            {
                double midDir;
                if( dir12 < dir23 )
                {
                    midDir = (dir12 + dir23 - 180) / 2d;
                }
                else
                {
                    midDir = (dir12 + dir23 + 180) / 2d;
                }
                double degreesOffset = 5;

                // make polar coordinate, convert to cartesian, shift into
                // position, and convert back to polar. Bah.
                // it feels like there should be an easier way to do this,
                // but...
                shieldBuilder.add(Utils.polarFromCartesian(Utils.cartesianFromPolar(
                        Utils.createPolarCoordinate(midDir - degreesOffset, shieldDistance))
                        .shiftBy(c2.getXComponent(), c2.getYComponent())));
                shieldBuilder.add(Utils.polarFromCartesian(Utils.cartesianFromPolar(
                        Utils.createPolarCoordinate(midDir + degreesOffset, shieldDistance))
                        .shiftBy(c2.getXComponent(), c2.getYComponent())));
            }
            else
            // this point is concave; make one shield point
            {
                double midDir;
                if( dir12 > dir23 )
                {
                    midDir = (dir12 + dir23 - 180) / 2d;
                }
                else
                {
                    midDir = (dir12 + dir23 + 180) / 2d;
                }

                shieldBuilder.add(Utils.polarFromCartesian(Utils.cartesianFromPolar(
                        Utils.createPolarCoordinate(midDir, shieldDistance)).shiftBy(
                        c2.getXComponent(), c2.getYComponent())));
            }
        }

        Utils.PolarCoordinate[] shieldShape = new Utils.PolarCoordinate[shieldBuilder.size()];
        for( int o = 0; o < shieldBuilder.size(); o++ )
        {
            shieldShape[o] = shieldBuilder.get(o);
        }

        return shieldShape;
    }
}
